package com.system.es;

import com.alibaba.fastjson.JSONObject;
import com.component.domain.Content;
import com.component.domain.MChannel;
import com.component.domain.Note;
import com.component.service.ContentService;
import com.component.service.NoteService;
import com.system.common.ContextUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.lucene.search.function.CombineFunction;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.*;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Component
public class NoteEsServer {

    private static String IndexName="note_data";

    @Autowired
    private EsServer esServer;

    @Autowired
    private NoteService noteService;

    @Autowired
    private ContentService contentService;

    @Autowired
    @Qualifier("restHighLevelClient")
    private RestHighLevelClient client;


    public Map<String,Object> page(String keyword,Integer pageNum,Integer pageSize) throws IOException {
        //1.创建 SearchRequest搜索请求,并指定要查询的索引
        SearchRequest searchRequest = new SearchRequest(IndexName);

        //2.创建 SearchSourceBuilder条件构造。
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.functionScoreQuery(QueryBuilders.boolQuery()
                .should(QueryBuilders.matchQuery("title", keyword).boost(3.0f))  // 设置权重大的字段
                .should(QueryBuilders.matchQuery("tag", keyword).boost(2.3f))  // 设置权重略低的字段
                .should(QueryBuilders.matchQuery("channelName", keyword).boost(2.2f))
                .minimumShouldMatch(1)  // 指定至少有一个should子句匹配
        ).boostMode(CombineFunction.SUM)  // 设置权重计算模式为加和
                .setMinScore(3.0f));  // 设置最小分数阈值，低于此阈值的结果会受到影响

      //  SortBuilder creationDateSort = SortBuilders.fieldSort("createDate").order(SortOrder.DESC);


       // searchSourceBuilder.sort(creationDateSort);
        searchSourceBuilder.from((pageNum - 1) * pageSize); // 起始位置
        searchSourceBuilder.size(pageSize); // 分页大小

        //自定义高亮 查找
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("*",20,20);
        highlightBuilder.requireFieldMatch(false);//如果要多个字段高亮,这项要为false
        highlightBuilder.preTags("<font class='hight'>");
        highlightBuilder.postTags("</font>");

        searchSourceBuilder.highlighter(highlightBuilder);

        //3.将 SearchSourceBuilder 添加到 SearchRequest中
        searchRequest.source(searchSourceBuilder);

        //4.执行查询
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

        //5.解析查询结果
        System.out.println("花费的时长：" + searchResponse.getTook());
        SearchHits hits = searchResponse.getHits();
        System.out.println("符合条件的总文档数量：" + hits.getTotalHits().value);
        if((String.valueOf(keyword).length() < 4 && hits.getTotalHits().value < 20) || hits.getTotalHits().value==0){
            System.out.println("开始使用内容搜索了");
            //进行文章内容搜索
            SearchSourceBuilder content = new SearchSourceBuilder();
            content.query(QueryBuilders.functionScoreQuery(QueryBuilders.boolQuery()
                    .should(QueryBuilders.matchQuery("title", keyword).boost(3.0f))  // 设置权重大的字段
                    .should(QueryBuilders.matchQuery("tag", keyword).boost(2.3f))  // 设置权重略低的字段
                    .should(QueryBuilders.matchQuery("channelName", keyword).boost(2.2f))
                    .should(QueryBuilders.matchQuery("content", keyword).boost(2.2f))
                    .minimumShouldMatch(1)  // 指定至少有一个should子句匹配
            ).boostMode(CombineFunction.SUM)  // 设置权重计算模式为加和
                    .setMinScore(2.2f));  // 设置最小分数阈值，低于此阈值的结果会受到影响

           // SortBuilder create = SortBuilders.fieldSort("createDate").order(SortOrder.DESC);
         //   content.sort(create);
            content.from((pageNum - 1) * pageSize); // 起始位置
            content.size(pageSize); // 分页大小
            content.highlighter(highlightBuilder);
            searchRequest.source(content);
            searchResponse= client.search(searchRequest, RequestOptions.DEFAULT);
        }
        hits = searchResponse.getHits();

        List<Map<String,Object>> maps = new ArrayList<>();
        hits.forEach(p -> {
            System.out.println("文档原生信息：" + p.getSourceAsString());
            System.out.println("高亮信息：" + p.getHighlightFields());
            Map<String, Object> map=p.getSourceAsMap();
            Map<String, HighlightField> m=p.getHighlightFields();
            StringBuilder stringBuilder=new StringBuilder();
            for(Map.Entry<String,HighlightField> mh: m.entrySet()){
                String key=mh.getKey();
                System.out.println("key "+key);
                HighlightField highlightField=mh.getValue();
                Text[] texts=highlightField.getFragments();
                for (int i = 0; i <texts.length ; i++) {
                    Text text=texts[i];
                    stringBuilder.append(text.string());
                }
            }
            String str= stringBuilder.toString().replace("/*", "")
                    .replace("*/","")
                    .replace("<style","")
                    .replace("/style>","")
                    .replace("style>","")
                    .replace("<script","")
                    .replace("/script>","")
                    .replace("script>","")
                    .replace("<template","")
                    .replace("template>","")
                    .replace("<title","")
                    .replace("title>","")
                    .replace("<html","")
                    .replace("html>","")
                    .replace("<body","")
                    .replace("body>","")
                    .replace("<head","")
                    .replace("head>","")
                    .replace("<img","")
                    .replace("v-","")
                    .replace("#","")
                    .replace(":","");
            map.remove("content");
            map.put("hights",str);
            maps.add(map);
        });
        Map<String,Object> map=new HashMap<>();
        map.put("data",maps);
        map.put("total",hits.getTotalHits().value);
        map.put("keyword",keyword);
        return map;
    }



    public Map<String,Object> pageHightLightFuzzSearch(String keyword,Integer pageNum,Integer pageSize) throws IOException {
        //1.创建 SearchRequest搜索请求,并指定要查询的索引
        SearchRequest searchRequest = new SearchRequest(IndexName);

        //2.创建 SearchSourceBuilder条件构造。
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //分词查询字段

        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

        // boolQuery.should(QueryBuilders.matchQuery("title", search).fuzziness(Fuzziness.AUTO).boost(5.0f).analyzer("ik_smart"));

        //boolQuery.should(QueryBuilders.matchPhraseQuery("title", search).boost(4.0f).analyzer("ik_smart"));
        // boolQuery.should(QueryBuilders.matchPhraseQuery("tag",search).boost(3.2f).analyzer("ik_smart"));
        //boolQuery.should(QueryBuilders.matchPhraseQuery("content", search).boost(1.0f).analyzer("ik_smart"));
        // boolQuery.should(QueryBuilders.matchPhraseQuery("channelName", search).boost(2.0f).analyzer("ik_smart"));

        boolQuery.should(QueryBuilders.functionScoreQuery(QueryBuilders.matchQuery("title",keyword), ScoreFunctionBuilders.weightFactorFunction(4)));
        boolQuery.should(QueryBuilders.functionScoreQuery(QueryBuilders.matchQuery("tag",keyword), ScoreFunctionBuilders.weightFactorFunction(3)));
        boolQuery.should(QueryBuilders.functionScoreQuery(QueryBuilders.matchQuery("channelName",keyword), ScoreFunctionBuilders.weightFactorFunction(2)));
        boolQuery.should(QueryBuilders.functionScoreQuery(QueryBuilders.matchQuery("content",keyword), ScoreFunctionBuilders.weightFactorFunction(1)));


        searchSourceBuilder.query(boolQuery);
        //  searchSourceBuilder.sort(SortBuilders.fieldSort("viewnum").order(SortOrder.DESC));
        //searchSourceBuilder.sort(SortBuilders.fieldSort("createDate").order(SortOrder.DESC));

        searchSourceBuilder.from((pageNum - 1) * pageSize); // 起始位置
        searchSourceBuilder.size(pageSize); // 分页大小

        //自定义高亮 查找
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("*",20,20);
        highlightBuilder.requireFieldMatch(false);//如果要多个字段高亮,这项要为false
        highlightBuilder.preTags("<font color='red'>");
        highlightBuilder.postTags("</font>");

        searchSourceBuilder.highlighter(highlightBuilder);

        //3.将 SearchSourceBuilder 添加到 SearchRequest中
        searchRequest.source(searchSourceBuilder);

        //4.执行查询
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

        //5.解析查询结果
        System.out.println("花费的时长：" + searchResponse.getTook());

        SearchHits hits = searchResponse.getHits();
        System.out.println("符合条件的总文档数量：" + hits.getTotalHits().value);

        List<Map<String,Object>> maps = new ArrayList<>();
        hits.forEach(p -> {
            System.out.println("文档原生信息：" + p.getSourceAsString());
            System.out.println("高亮信息：" + p.getHighlightFields());
            Map<String, Object> map=p.getSourceAsMap();
            Map<String, HighlightField> m=p.getHighlightFields();

            StringBuilder stringBuilder=new StringBuilder();
            for (HighlightField h:m.values()){
                Text[] texts=h.getFragments();
                for (int i = 0; i <texts.length ; i++) {
                    Text text=texts[i];
                    stringBuilder.append(text.string());
                }
                if (stringBuilder.length() > 50) {
                    // 使用substring方法对StringBuilder进行截取
                    stringBuilder.setLength(50);
                }
            }
            String s= map.get("content")==null ? "": (String)map.get("content");
            StringBuilder c=new StringBuilder(s);

            if (c.length() > 50) {
                // 使用substring方法对StringBuilder进行截取
                c.setLength(50);
            }
            map.put("content",c.toString());
            map.put("hights",stringBuilder.toString());
            maps.add(map);
        });
        Map<String,Object> map=new HashMap<>();
        map.put("data",maps);
        map.put("total",hits.getTotalHits().value);
        map.put("keyword",keyword);
        return map;
    }

    /**
     * 加载数据
     * @throws IOException
     */
    public void loadData() throws IOException {
        boolean b=esServer.exitIndex(IndexName);
        if(!b){
            esServer.createIndex(IndexName);
        }
        List<Note> list=noteService.list();
        if(list!=null){
            list.forEach(e->{
                com.system.es.dto.Note note=new com.system.es.dto.Note();
                note.setCreateDate(e.getCreateTime());
                Content content=contentService.getContentByNid(e.getId());
                if(content.getIsencrypt()){
                    String contStr= ContextUtils.decryptStr(content.getContent());
                    content.setContent(contStr);
                    note.setContent(contStr);
                }
                if(e.getmChannel()!=null){
                    MChannel mChannel=e.getmChannel();
                    note.setChannelId(mChannel.getId().toString());
                    note.setChannelName(mChannel.getChannelname());
                }
                BeanUtils.copyProperties(e,note);
                Map<String,Object> data= null;
                try {
                    data = esServer.getDocumentById(IndexName,note.getId().toString());
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
                if(data==null){
                    try {
                        esServer.addDocument(note,IndexName);
                    } catch (Exception exception) {
                        exception.printStackTrace();
                    }
                }else {
                    try {
                        esServer.updateDocumentById(note,IndexName);
                    } catch (Exception exception) {
                        exception.printStackTrace();
                    }
                }
            });
        }
    }

}
